/*
 * spr.c
 * Part of the !Memphis distribution
 * (c) bdb/nas, 1991-3
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"
/* #include "_swis.h" */
#include "sprite.h"
#include "interface.h"
#include "core.h"

#include "spr.h"
static int pagesize = 0x8000;
static int deadsize = 0;
static sprite_area *ss;
static int sssize;
static int ssfreeoff;
static int desired = 0;
static SPRITENAME deadname={ "mfsxdeadspr" }; /* Dead sprites names get 1st 4 chars set to this. */
#define DEADMARK (*(int *)&deadname)

static void readarea(void)
{
  _kernel_swi_regs r;
  
  r.r[0] = 3;
  _kernel_swi(XOS_Bit + OS_ReadDynamicArea, &r, &r);
  ss = (sprite_area *) r.r[0];
  sssize = r.r[1];
  ssfreeoff = sssize?ss->freeoff:sizeof(sprite_area);
  _kernel_swi(XOS_Bit + OS_ReadMemMapInfo, &r, &r);
  pagesize = r.r[0];
}

/*{{{  */ static _kernel_oserror *growsprites( int grow ) /* ensure extra bytes in sprite area, 0=ok*/
{ _kernel_oserror *err;
  _kernel_swi_regs r;

  readarea();
  grow -= sssize - ssfreeoff;
  if ( grow <= 0 )
    return 0;
  r.r[0] = 3;
  r.r[1] = -(-grow & ~(pagesize-1));
  err = _kernel_swi(XOS_Bit + OS_ChangeDynamicArea, &r, &r);
  readarea();
  if (err)
    printf("**************Failed to grow sprite area by %d bytes********",-(-grow & ~(pagesize-1)));
  return err;
}
/*}}}  */
/*{{{  */ static _kernel_oserror *shrinksprites( void ) /* Shrink sprite area if possible/desirable*/
{ int shrink;
  _kernel_oserror *err;
  _kernel_swi_regs r;

  readarea();
  desired = ( _kernel_osbyte( 161, 147, 0 ) >> 8 & 0xFF ) * pagesize;
  shrink = sssize - ssfreeoff - desired;
  shrink = -(-shrink&~(pagesize-1));
  if ( shrink <= 0 )
    return NULL;
  r.r[0] = 3;
  r.r[1] = -shrink;
  err=_kernel_swi(XOS_Bit + OS_ChangeDynamicArea, &r, &r);
  readarea();
  return err;
}
/*}}}  */
/*{{{  */ static void deletedeadsprites( void )/**/
{ sprite_header *h,*ch;
  int hsize;
  readarea();
if (sssize)
{ for ( ch = h = ( sprite_header * )( ( char * )ss + ss->sproff );
        h < ( sprite_header * )( ( char * )ss + ssfreeoff );
        h = ( sprite_header * )( ( char * )h + hsize ) )
  { hsize = h->next;
    if ( ( ( int * )&h->name )[0] != DEADMARK )
    { if (h != ch)
        memmove(ch, h, hsize);
      ch = (sprite_header *)((char *)ch + hsize);
    }
    else
      ss->number--;
  }
  ssfreeoff = ss->freeoff = (char *)ch - (char *)ss;
}
deadsize = 0;
}
/*}}}  */
/*{{{  */ sprite_header *findsprite( SPRITENAME name ) /* find sprite or NULL*/
{ sprite_header *h;
  readarea();
 if (sssize)
  for ( h = ( sprite_header * )( ( char * )ss + ss->sproff );
        h < ( sprite_header * )( ( char * )ss + ssfreeoff );
        h = ( sprite_header * )( ( char * )h + h->next ) )
    if ( ( ( int * )&name )[0]==( ( int * )&h->name )[0] &&
         ( ( int * )&name )[1]==( ( int * )&h->name )[1] &&
         ( ( int * )&name )[2]==( ( int * )&h->name )[2] )
      return h;
  return NULL;
}
/*}}}  */
/*{{{  */ _kernel_oserror *createsprite( SPRITENAME name, int size, sprite_header **rh ) /* create sprite */
{ sprite_header *h;
  _kernel_oserror *err;
  size = ( size+3 )&~3;
  if (deadsize > 0)
    deletedeadsprites();
  err=growsprites( SHSIZE + size );
  if (err) return err;
  h = ( sprite_header * )( ( char * )ss + ssfreeoff );
  h->next = SHSIZE + size;
  *( SPRITENAME * )&h->name = name;
  h->width = 0;
  h->height = size/4 - 1;
  h->lbit = 0;
  h->rbit = 31;
  h->image = SHSIZE;
  h->mask = SHSIZE;
  h->mode = 18;
  ss->freeoff += h->next;
  ss->number++;
  *rh = h;
  return NULL;
}
/*}}}  */
/*{{{  */ _kernel_oserror *setspritesize( sprite_header *h, int size ) /* set size */
{ _kernel_oserror *err;
  int oldsize;
#ifdef DEBUG
  printf( "setspritesize %p %x\n", h, size );
#endif
  if ( !h )
    return NULL;
  oldsize = h->next-SHSIZE;
  size = ( size+3 )&~3;
  if ( size>oldsize )
  { err = growsprites( size-oldsize );
    if (err) return err;
    memmove( ( char * )h + SHSIZE + size,
             ( char * )h + h->next,
             ( char * )ss + ssfreeoff - ( ( char * )h + h->next ) );
    ss->freeoff += size-oldsize;
    h->height = size/4 - 1;
    h->next = SHSIZE + size;
  }
  else
  if ( size + SHSIZE+4 <= oldsize )     /* Room to truncate */
  { h->height = size/4 - 1;
    h->next = SHSIZE + size;
    h = (sprite_header *)((char *)h+h->next);
    size = oldsize - size - SHSIZE;
    h->next = SHSIZE + size;
    *( SPRITENAME * )&h->name = deadname;
    h->width = 0;
    h->height = size/4 - 1;
    h->lbit = 0;
    h->rbit = 31;
    h->image = SHSIZE;
    h->mask = SHSIZE;
    h->mode = 18;
    deletesprite(h);
  } 
  return NULL;
}
/*}}}  */
/*{{{  */ _kernel_oserror *deletesprite( sprite_header *h ) /* mark as dead and maybe deletedead*/
{  if ( !h )
    return NULL;
  ( ( int * )&h->name )[0] = DEADMARK;
  deadsize += h->next;
  if ( sssize - ssfreeoff + deadsize >= pagesize )
  { deletedeadsprites();
    return shrinksprites();
  }
  return NULL;
}
/*}}}  */
_kernel_oserror *spritefreespace( struct freespace *b)
{
  readarea();
  if (deadsize)
    deletedeadsprites();
  if (sssize)
  { b->free = ss->size - ss->freeoff;
    b->biggest = b->free;
    b->size = ss->size;
  }
  else
    b->free = b->biggest = b->size = 0;
  return NULL;
}
